<template>
  <div
    class="draggable_resizable_rotatable"
    ref="dragRef"
    @pointerdown.stop="mousedown('drag')"
    :style="style"
    tabindex="1"
    :class="handleStatusStyle"
    :data-drag="rect.id"
    @contextmenu="contextmenu"
  >
    <template v-if="active">
      <!-- 调整大小 -->
      <div
        class="resize-handle"
        v-for="(item, i) in handles"
        :key="item"
        ref="resizeHandleRef"
        @pointerenter="resizeHandleMouseenter(i)"
        @pointerleave.self="resetCursor(i)"
        :class="[item]"
        @pointerdown.stop="mousedown(item)"
      ></div>
      <!-- 旋转 -->
      <template v-if="rect.rotatable">
        <div
          class="rotation"
          ref="rotationHandleRef"
          @mouseenter.self="rotationHandleMouseenter(i)"
          @pointerleave.self="resetCursor(i)"
          v-for="(item, i) in rotationHandles"
          :key="item"
          :class="[item]"
          @pointerdown.stop="mousedown(item)"
        ></div>
      </template>
    </template>

    <div :style="contentStyle" class="drag-content">
      <!-- 插槽 -->
      <slot></slot>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, type CSSProperties, onMounted, useTemplateRef } from "vue";
import resizeCursor from "./assets/resize.svg";
import rotationCursor from "./assets/rotation.svg";
import { diagonalSize, resizeHandles, rotationHandles } from "./core/handle";
import { useDragData, useStyle } from "./core/hooks";
import type { EmitKey, HandleType, IDraggable } from "./core/types";
import { DomUtils } from "./utils/DomUtils";
import { MathUtils } from "./utils/MathUtils";
import { MatrixUtils } from "./utils/MatrixUtils";
import { OBB } from "./utils/OBBUtils";

const { snapPoint, startCorners, rect, positionInfo, mouseRecord, temporaryData, setPositionInfo } =
  useDragData();
const { contentStyle, handleStatusStyle } = useStyle(rect);
const dragRef = useTemplateRef<HTMLElement>("dragRef");
const resizeHandleRef = useTemplateRef<HTMLElement[]>("resizeHandleRef");
const rotationHandleRef = useTemplateRef<HTMLElement[]>("rotationHandleRef");

const active = defineModel<boolean>("active");

const emit = defineEmits<{
  (e: EmitKey, data: any): void;
}>();

const props = defineProps<{
  controller: IDraggable;
  data?: unknown;
  zoom: number;
}>();
function setOriginPosition() {
  const [originPosition] = MatrixUtils.calculateRotatedCorners({
    ...props.controller,
    x: mouseRecord.rx,
    y: mouseRecord.ry
  });

  props.controller.originX = Math.round(originPosition.x);
  props.controller.originY = Math.round(originPosition.y);
}
const mousedown = (key: HandleType) => {
  const event = window.event as MouseEvent;
  const { w, h, x, y, rotate } = props.controller;

  const { zoom } = props;
  if (props.controller.disabled) return;
  /* 鼠标按下时的坐标，和鼠标移动中的EVENT配合计算鼠标拖拽移动的位移 */
  mouseRecord.sx = x;
  mouseRecord.sy = y;
  mouseRecord.sw = w;
  mouseRecord.sh = h;
  emit("selected", props.controller);
  /* 判断是哪种行为 */
  temporaryData.value.behavior = key;
  // 获取矩形中心点，用于计算旋转角度
  const { centerX, centerY } = DomUtils.getCenter(dragRef.value!)!;
  const startAngle = MathUtils.getAngle(centerX, centerY, event.x, event.y);
  /* 当非旋转的行为，记录之前的顶点 */

  if (!key.includes("rotation")) {
    startCorners.value = MatrixUtils.calculateRotatedCorners(props.controller);

    startCorners.value.forEach((v) => {
      v.x -= mouseRecord.vx;
      v.y -= mouseRecord.vy;
    });
  } else {
    mouseRecord.startAngle = startAngle * -1 + rotate;
    mouseRecord.vx = 0;
    mouseRecord.vy = 0;
  }

  setPositionInfo(dragRef, positionInfo, zoom);
  /* 鼠标滑动 */
  const mousemove = (me: MouseEvent) => {
    const { grid, resizable, draggable, parent } = props.controller;
    const dx = (me.x - event.x) / zoom;
    const dy = (me.y - event.y) / zoom;
    let gr = grid[2] || 1;
    const { behavior } = temporaryData.value;

    /** @description
     * 部分点调整后要重新计算原点
     * 用左上角点作为原点
     * 计算旋转后的中心点
     *
     */
    if (behavior?.includes("rotation")) {
      /**
       * @description 计算鼠标滑动的角度
       * @param {number} angleDiff 鼠标滑动的角度
       *  */
      const currentMouseAngle = MathUtils.getAngle(centerX, centerY, me.x, me.y);

      const angleDiff = currentMouseAngle + mouseRecord.startAngle;
      if (Math.abs(rotate) === 360) {
        mouseRecord.startAngle = 0;
        props.controller.rotate = 360;
      }
      const rotation = snapPoint.find((v) => Math.abs(v - angleDiff) < Math.max(gr, 4));
      const angle = MathUtils.normalizeRotation(MathUtils.roundToStep(angleDiff, gr) % 360);
      const [minX, minY, maxX, maxY] = MatrixUtils.calculateBounds({
        ...props.controller,
        rotate: angle
      });
      if (parent && dragRef.value?.parentElement) {
        const { width: parentWidth, height: parentHeight } = DomUtils.getDomBoundary(
          dragRef.value?.parentElement,
          zoom
        )!;
        if (minX < 0 || minY < 0 || maxX > parentWidth || maxY > parentHeight) {
          return;
        }
      }

      if (gr === 1 && (rotation || rotation === 0)) {
        props.controller.rotate = rotation;
      } else {
        props.controller.rotate = angle;
      }

      emit("rotate", props.controller);
    }

    if (resizable && behavior?.includes("resize")) {
      OBB.isDragSizeOutBoundary({
        rect: props.controller,
        mouseRecord,
        key,
        dx,
        dy,
        el: dragRef.value
      });
      // actionHandlers[key]?.()
      emit("resize", props.controller);
    }
    if (draggable && behavior === "drag") {
      OBB.detectingMovementOutOfBounds({
        rect: props.controller,
        el: dragRef,
        mouseRecord,
        dx,
        dy,
        zoom
      });
      emit("drag", props.controller);
    }
    setPositionInfo(dragRef, positionInfo, zoom);

    setOriginPosition();
  };

  const mouseup = () => {
    const { x, y, w, h } = props.controller;
    const { behavior } = temporaryData.value;
    /* 用于记录偏移的信息 */
    mouseRecord.sx = x;
    mouseRecord.sy = y;
    mouseRecord.sw = w;
    mouseRecord.sh = h;

    if (behavior?.includes("rotation") || behavior?.includes("resize")) {
      props.controller.x -= mouseRecord.vx;
      props.controller.y -= mouseRecord.vy;
      // mouseRecord.vx = 0
      // mouseRecord.vy = 0
    }
    setPositionInfo(dragRef, positionInfo, zoom);
    removeEventListener("mousemove", mousemove);
    removeEventListener("mouseup", mouseup);
  };
  addEventListener("mousemove", mousemove);
  addEventListener("mouseup", mouseup);
};
const resizeHandleMouseenter = async (index: number) => {
  const offsetRotate = [90, 0, 90, 0, -45, 45, 45, -45];
  if (resizeHandleRef.value) {
    const cursorUrl = await DomUtils.setRotatedCursor(
      resizeCursor,
      props.controller.rotate + offsetRotate[index]
    );
    if (!resizeHandleRef.value[index]) return;
    resizeHandleRef.value[index].style.cursor =
      `url(${cursorUrl.src}) ${cursorUrl.x} ${cursorUrl.y},auto`;
  }
};
const resetCursor = (index: number) => {
  if (resizeHandleRef.value) resizeHandleRef.value[index].style.cursor = "auto";
};
const setStartCorners = () => {
  const { behavior } = temporaryData.value;
  /* 更新前的四个顶点 */
  const [topLeft] = MatrixUtils.calculateRotatedCorners(props.controller);
  /* 矩形的信息 */
  const { x, y } = props.controller;
  /* 更新后的四个顶点，但x,y均没有更新 */
  const [tl, tr, bl, br] = startCorners.value;
  let vx = 0;
  let vy = 0;
  // 方向配置映射
  const directionConfig: Partial<{
    [key in HandleType]: () => { vx: number; vy: number };
  }> = {
    "resize-mr": () => ({ vx: topLeft.x - tl.x, vy: topLeft.y - tl.y }),
    "resize-mb": () => ({ vx: topLeft.x - tl.x, vy: topLeft.y - tl.y }),
    "resize-br": () => ({ vx: topLeft.x - tl.x, vy: topLeft.y - tl.y }),
    "resize-ml": () => ({ vx: topLeft.x + tr.x, vy: topLeft.y + tr.y }),
    "resize-bl": () => ({ vx: topLeft.x + tr.x, vy: topLeft.y + tr.y }),
    "resize-mt": () => {
      props.controller.originX = topLeft.x;
      props.controller.originY = topLeft.y;
      return { vx: topLeft.x + bl.x, vy: topLeft.y + bl.y };
    },
    "resize-tl": () => ({ vx: topLeft.x + bl.x, vy: topLeft.y + bl.y }),
    "resize-tr": () => ({ vx: topLeft.x + br.x, vy: topLeft.y + br.y })
  };

  // 获取对应方向的配置
  const config = directionConfig[behavior];
  if (config) {
    const { vx: calculatedVx, vy: calculatedVy } = config();
    vx = calculatedVx;
    vy = calculatedVy;
  }
  mouseRecord.vx = vx;
  mouseRecord.vy = vy;

  const updatedPos = {
    ...props.controller,
    x: x - mouseRecord.vx,
    y: y - mouseRecord.vy
  };

  /** @description 旋转后的顶点
   * 顶点偏移量 = 旋转后的顶点-旋转前的顶点
   */

  startCorners.value = MatrixUtils.calculateRotatedCorners(updatedPos);
};
const contextmenu = () => {
  emit("contextmenu", event);
};

const rotationHandleMouseenter = async (index: number) => {
  if (rotationHandleRef.value) {
    const offsetRotate = [45, 135, -135, -45];
    const cursorUrl = await DomUtils.setRotatedCursor(
      rotationCursor,
      props.controller.rotate + offsetRotate[index]
    );
    rotationHandleRef.value[index].style.cursor =
      `url(${cursorUrl.src}) ${cursorUrl.x} ${cursorUrl.y},auto`;
  }
};

const style = computed((): CSSProperties => {
  /* 矩形的信息 */
  const { x, y, w, h, rotate, z } = props.controller;
  /* 更新后的四个顶点，但x,y均没有更新 */
  const { behavior } = temporaryData.value;

  /* 八个方向拖拽,仅当拖拽调整大小才会更新偏移顶点 */
  if (behavior?.includes("resize")) {
    if (active.value) {
      setStartCorners();
    }
  }
  mouseRecord.rx = x - mouseRecord.vx;
  mouseRecord.ry = y - mouseRecord.vy;

  return {
    width: `${w}px`,
    height: `${h}px`,
    transform: `
    translate(${mouseRecord.rx}px,${mouseRecord.ry}px)
    rotate(${rotate}deg)`,
    zIndex: z
  };
});

const handles = computed(() => {
  const { resizable } = props.controller;
  if (!resizable) {
    return resizeHandles;
  }
  return resizeHandles.concat(diagonalSize);
});
onMounted(() => {
  onClickOutside(dragRef.value, () => {
    if (!props.controller.preventDeactivation) {
      active.value = false;
    }
  });
});
</script>
<style lang="scss">
@import "./css/style.scss";
</style>
